﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using nGSO.Model;
using nGSO.Performance;
using System.Drawing;
using System.Diagnostics;

namespace nGSO.UI
{
    public class ResultToChart : IDisposable
    {
        private DataModelEntities _ctx = null;

        public ResultToChart()
        {
            _ctx = PerfDatabase.GetContext();
        }

        public event LoadRaportStateHandler OnRaportStateChanged;

        /// <summary>
        /// Generates chart form cases grouped by selected gso parameters
        /// </summary>
        /// <param name="selCase">Parameter which were used for GSO alg</param>
        /// <returns>Line chart</returns>
        internal void GenerateEffeciencyByCase(AnalyticsOptions selCase, Panel container)
        {
            container.Invoke((MethodInvoker)delegate
            {
                container.Controls.Clear();
            }); 
            
            var allCasesByParams = from t in _ctx.PerfTests
                                   join d in _ctx.PerfEntryHistory on t.PerfTestId equals d.PerfTestId
                                   where t.FunctionKey == selCase.FunctionKey &&
                                            t.DesiredNoOfNeighbors == selCase.DesiredNoOfNeighbors &&
                                            t.MaxIterations == selCase.MaxIterations &&
                                            t.Population == selCase.Population &&
                                            t.RadialSensorRange == selCase.RadialSensorRange
                                   select new { d.PerfEntryId, d.Effecinity, d.Iteration, t.GsoVer };


            Chart chart = new Chart() { Width = 700 };
            ChartArea area = new ChartArea();

            area.AxisX.Title = "Iteration";
            area.AxisX.Minimum = 0;
            area.AxisX.Maximum = selCase.MaxIterations;

            area.AxisY.Title = "Effeciency";
            

            chart.ChartAreas.Add(area);
            chart.Legends.Add(new Legend() { });
            chart.Titles.Add("Efficiency by iteration");

            var groupedByGsoVer = allCasesByParams.ToList().GroupBy(s => s.GsoVer);

            double maxGlobalEffeciency = 0;
            foreach (var gsoType in groupedByGsoVer) // gsoversion
            {
                Series serie = GetSerieFromGsoType(gsoType.Key);

                
                for (int i = 0; i < selCase.MaxIterations; i++) 
                {
                    var allIterationCases = gsoType.Where(s => s.Iteration == i);

                    double avgIterationRes = allIterationCases.Average(z => z.Effecinity); // avg effeciency at iteration by multiple case results

                    if (maxGlobalEffeciency < avgIterationRes)
                    {
                        maxGlobalEffeciency = avgIterationRes;
                    }

                    DataPoint point = new DataPoint(i, avgIterationRes);
                    serie.Points.Add(point);
                    
                }
                
                chart.Series.Add(serie);

            }
            area.AxisY.Maximum = maxGlobalEffeciency;

            foreach (var serie in chart.Series)
            {
                AttachMostEffectivePoint(chart, serie);
            }

            
            container.Invoke((MethodInvoker)delegate
            {
                container.Controls.Add(chart);
            }); 
        }

        private static void AttachMostEffectivePoint(Chart chart, Series serie)
        {
            CalloutAnnotation bestPointAnn = new CalloutAnnotation();
            DataPoint p = chart.Series[serie.Name].Points.FindMaxByValue("Y1");
            bestPointAnn.AnchorDataPoint = p;
            bestPointAnn.Text = serie.Name  + " " + p.XValue + " effeciency " + String.Format("{0:0.000}", p.YValues[0])  ;
            bestPointAnn.BackColor = Color.FromArgb(255, 255, 128);
            bestPointAnn.ClipToChartArea = "Default";
            bestPointAnn.AllowMoving = true;
            bestPointAnn.AllowAnchorMoving = true;
            chart.Annotations.Add(bestPointAnn);
        }

        public void CompareGsoVerWithPopulation(string funKey, Panel container)
        {
            ChangeStateInfo("Loading GSO version comparsion");

            using (DataModelEntities ctx = PerfDatabase.GetContext())
            {
                var casesByPopulation = ctx.PerfTests.Where(s => s.FunctionKey == funKey).GroupBy(s => s.Population);

                foreach (var population in casesByPopulation) //population
                {
                    ChangeStateInfo("Loading population " + population.Key);
                    Chart chart = new Chart();
                    ChartArea chartArea1 = new ChartArea();
                    chartArea1.AxisX.Title = "Population";
                    chartArea1.AxisY.Title = "Normalized Efficiency";
                    string chartName = funKey + "_" + population.Key;

                    chart.Titles.Add(chartName);
                    chart.ChartAreas.Add(chartArea1);
                    //chartArea1.AxisX.Minimum = 400;

                    chart.Legends.Add(new Legend() { });

                    foreach (var gsoType in population.GroupBy(s => s.GsoVer)) // gsoversion
                    {
                        ChangeStateInfo("Loading version " + gsoType.Key);
                        Series serie = chart.Series.Add(gsoType.Key);
                        serie.ChartType = SeriesChartType.FastPoint;
                        serie.MarkerSize = 12;

                        foreach (var grpIteration in gsoType.GroupBy(s => s.MaxIterations)) //iterations
                        {
                            double miny = (double)grpIteration.Min(s => s.OverallEfficiency);
                            double maxy = (double)grpIteration.Max(s => s.OverallEfficiency);
                            double avgy = (double)grpIteration.Average(s => s.OverallEfficiency);

                            serie.Points.Add(new DataPoint(grpIteration.Key, new double[] { avgy }));
                        }
                    }

                    container.Invoke((MethodInvoker)delegate
                    {
                        container.Controls.Add(chart);
                    }); 
                }
            }
        }

        public void IterationGSOVerComparsion(string funKey, Panel pnlIterationEffecinity)
        {
            ChangeStateInfo("Iteration comare by GSO versions");

            using (DataModelEntities ctx = PerfDatabase.GetContext())
            {
                var casesBypopulation = ctx.PerfTests.Where(s => s.FunctionKey == funKey).GroupBy(s=>s.Population); // functions
                
                foreach (var populationGrp in casesBypopulation) // population
                {
                    int populationSize = populationGrp.Key;
                    ChangeStateInfo("Get population size " + populationSize);

                    foreach (var iterGrp in populationGrp.GroupBy(s => s.MaxIterations)) // iterations
                    {
                        ChangeStateInfo("Get group iterations " + iterGrp.Key);
                        Chart chart = new Chart() { Width = 700 };
                        ChartArea area = new ChartArea();
                        
                        area.AxisX.Title = "Iteration";
                        area.AxisX.Minimum = 0;
                        area.AxisX.Maximum = iterGrp.Key;

                        area.AxisY.Title = "Effecinity";
                        area.AxisY.Maximum = populationGrp.Max(s => s.OverallEfficiency).Value;

                        chart.Legends.Add(new Legend() { });



                        string chartName = funKey + "_P:" + populationSize + "_I:" + iterGrp.Key + " ";
                        
                        chart.Titles.Add(chartName);
                        
                        chart.ChartAreas.Add(area);

                        foreach (var gsoVer in populationGrp.GroupBy(s => s.GsoVer)) // version
                            AddVersionPoints(funKey, ctx, populationSize, chart, gsoVer);

                        pnlIterationEffecinity.Invoke((MethodInvoker)delegate
                        {
                            pnlIterationEffecinity.Controls.Add(chart);
                        }); 
                    }
                }               
            }
        }

        private void AddVersionPoints(string funKey, DataModelEntities ctx, int populationSize, Chart chart, IGrouping<string, PerfTests> gsoVer)
        {
            string gsoVerTypeName = gsoVer.Key;
            ChangeStateInfo("Get group type " + gsoVerTypeName);
            
            Series serie = GetSerieFromGsoType(gsoVerTypeName);
            chart.Series.Add(serie);

            var cases = ctx.PerfTests.Where(s => s.GsoVer == gsoVerTypeName && s.Population == populationSize && s.FunctionKey == funKey);

            var historyEntries = cases.SelectMany(s => s.PerfEntryHistory);
            //var entriesByIteration = historyEntries.GroupBy(s => s.Iteration).OrderBy(s => s.Key);

            foreach (var histEntry in historyEntries.OrderBy(s=>s.Iteration))
            {
                double effecinity = histEntry.Effecinity;// histEntry.Average(s => s.Effecinity);
                double iteration = histEntry.Iteration;

                serie.Points.AddXY(iteration, effecinity);
            }
        }

        private void ChangeStateInfo(string message)
        {
            if (OnRaportStateChanged != null)
                OnRaportStateChanged(this, new LoadRaportEventArg(message));

        }

        private Series GetSerieFromGsoType(string gsoTypeName)
        {
            Series serie = new Series(gsoTypeName);
            serie.ChartType = SeriesChartType.Line;
            serie.MarkerSize = 2;

            switch (gsoTypeName)
            {
                case "normal":
                    serie.Color = Color.Blue;
                    serie.BorderDashStyle = ChartDashStyle.Solid;
                    break;
                case "gsov1":
                    serie.Color = Color.Red;
                    serie.BorderDashStyle = ChartDashStyle.Dot;
                    break;
                case "gsov2":
                    serie.Color = Color.Purple;
                    serie.BorderDashStyle = ChartDashStyle.Dash;
                    break;
                default:
                    break;
            }

            return serie;
        }





        #region IDisposable Members

        public void Dispose()
        {
            _ctx.Dispose();
        }

        #endregion
    }
}